#include "CVector2d.h"
#include "CFrustum.h"



const int32 CFrustum::NumberOfPlanes = CFrustum::Far + 1;



CFrustum::CFrustum()
{
	m_plane = new CPlane[ NumberOfPlanes ];
}


CFrustum::CFrustum( const CFrustum& f )
{
	m_plane = new CPlane[ NumberOfPlanes ];
	*this = f;
}

CFrustum& CFrustum::operator=( const CFrustum& other ) 
{
	for (int i=0; i<NumberOfPlanes; i++) {
		m_plane[i] = other.m_plane[i];
	}
	return *this;
}

CFrustum::CFrustum( const CVector3d* pts, fixed farPlaneDistanceFromApex )
{
	m_plane = new CPlane[ NumberOfPlanes ];
	Set( pts[0], pts[1], pts[2], pts[3], pts[4], farPlaneDistanceFromApex );
}

 
CFrustum::CFrustum( const CVector3d& apex, 
					const CVector3d& nearLeftTop, const CVector3d& nearRightTop, 
					const CVector3d& nearRightBottom, const CVector3d& nearLeftBottom,
					fixed farPlaneDistanceFromApex )
{
	m_plane = new CPlane[ NumberOfPlanes ];
	Set( apex, nearLeftTop, nearRightTop, nearRightBottom, nearLeftBottom, farPlaneDistanceFromApex );
}
	

void CFrustum::Set( const CVector3d& apex, 
					const CVector3d& nearLeftTop, const CVector3d& nearRightTop, 
					const CVector3d& nearRightBottom, const CVector3d& nearLeftBottom,
					fixed farPlaneDistanceFromApex )
{
	const CVector3d* p0 = &apex;
	const CVector3d* p1 = &nearLeftBottom;
	const CVector3d* p2 = &nearLeftTop;
		
	for( int32 i = 0; i < NumberOfPlanes; i++ )
	{
		switch( i )
		{
		case 1:
			p1 = &nearLeftTop;
			p2 = &nearRightTop;
			break;
		case 2:
			p1 = &nearRightTop;
			p2 = &nearRightBottom;
			break;
		case 3:
			p1 = &nearRightBottom;
			p2 = &nearLeftBottom;
			break;
		case 4:
			p0 = &nearLeftTop;
			p1 = &nearRightTop;
			break;
		case 5:
			{
				CVector3d f0( nearLeftBottom - nearLeftTop );
				f0.m_i >>= 1; f0.m_j >>= 1; f0.m_k >>= 1;
				f0 += nearLeftTop;
				CVector3d f1( nearRightBottom - nearRightTop );
				f1.m_i >>= 1; f1.m_j >>= 1; f1.m_k >>= 1;
				f1 += nearRightTop;
				f1 -= f0;
				f1.m_i >>= 1; f1.m_j >>= 1; f1.m_k >>= 1;
				f0 += f1;
				f0 -= apex;
				fixed nda = f0.Length();
				fixed ndaSQ = CMathFixed::Mul( nda, nda );

				fixed dist = ((CVector2d*)&nearLeftTop)->Length();
				dist = CMathFixed::Sqrt( CMathFixed::Mul( dist, dist ) + ndaSQ );
				
				f0 = ( nearLeftTop - apex );
				f0 = f0.Normalize() * CMathFixed::Mul( dist, farPlaneDistanceFromApex );

				f1.Set( -m_plane[4].m_n.m_i, -m_plane[4].m_n.m_j, -m_plane[4].m_n.m_k );

				m_plane[5].Set( f1, f1 * f0 );
			}
			break;
		}

		if( i != 5 )
			m_plane[ i ].Set( *p0, *p1, *p2 );
	}
}


CSpace::Relativity CFrustum::DetermineSpace( const CSphere& s ) const
{
	int32 count[ CSpace::NumRelations ] = { 0 };

	for( int32 i = NumberOfPlanes - 1; i >= 0; i-- )
	{
		CSpace::Relativity space = CSpace::Null;

		s.Intersects( m_plane[ i ], space );	

		if( space == CSpace::Outside ) 
			return CSpace::Outside;
		
		count[ space ]++;
	}

	return count[ CSpace::Inside ] == NumberOfPlanes ? CSpace::Inside : CSpace::Intersecting;
}
